﻿namespace StockSharp.Algo.Indicators.Oscillator
{
	using System;

	using StockSharp.Algo.Candles;
	using StockSharp.Algo.Indicators.Trend;

	/// <summary>
	/// Часть индикатора <see cref="DirectionalIndex"/>.
	/// </summary>
	public abstract class DiPart : LengthIndicator<decimal>
	{
        private Candle _lastCandle;
		private readonly AverageTrueRange _averageTrueRange;
		private readonly LengthIndicator<decimal> _movingAverage;
        private bool _isFormed = false;

		/// <summary>
		/// Инициализировать <see cref="DiPart"/>.
		/// </summary>
		protected DiPart()
		{
			_averageTrueRange = new AverageTrueRange(new WilderMovingAverage(), new TrueRange());
			_movingAverage = new WilderMovingAverage();
		}

		/// <summary>
		/// Длина периода.
		/// </summary>
		public override int Length
		{
			get
			{
				return _averageTrueRange.Length;
			}
			set
			{
				_averageTrueRange.Length = value;
				_movingAverage.Length = value;
				Reset();
			}
		}

		/// <summary>
		/// Сбросить состояние индикатора на первоначальное. Метод вызывается каждый раз, когда меняются первоначальные настройки (например, длина периода).
		/// </summary>
		public override void Reset()
		{
			base.Reset();
			_averageTrueRange.Reset();
			_movingAverage.Reset();
			_lastCandle = null;
            _isFormed = false;
		}

		/// <summary>
		/// Сформирован ли индикатор.
		/// </summary>
		public override bool IsFormed
		{
            get { return _isFormed; }
		}

		/// <summary>
		/// Возможно ли обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>True, если возможно, иначе, false.</returns>
		public override bool CanProcess(IIndicatorValue input)
		{
			return _movingAverage.CanProcess(input) && _averageTrueRange.CanProcess(input);
		}

		/// <summary>
		/// Обработать входное значение.
		/// </summary>
		/// <param name="input">Входное значение.</param>
		/// <returns>Результирующее значение.</returns>
		protected override decimal OnProcess(IIndicatorValue input)
		{
            decimal result = 0;

			var candle = input.GetValue<Candle>();
            
            // задержка в 1 период
            _isFormed = _averageTrueRange.IsFormed && _movingAverage.IsFormed;

            _averageTrueRange.Process(input);

			if (_lastCandle != null)
			{
                var trValue = _averageTrueRange.GetCurrentValue<decimal>();

				// не вносить в тернарный оператор! 
                var maValue = _movingAverage.Process(GetValue(candle, _lastCandle)).GetValue<decimal>();

                result = (trValue != 0m) ? (100m * maValue / trValue) : 0m;				
			}
			
            _lastCandle = candle;

            return result;
		}

		/// <summary>
		/// Получить значение части.
		/// </summary>
		/// <param name="current">Текущая свечка.</param>
		/// <param name="prev">Предыдущая свечка.</param>
		/// <returns>Значение.</returns>
		protected abstract decimal GetValue(Candle current, Candle prev);
	}
}